﻿//-------------------------------------------------------------------
//	Copyright (c) 2010-2014  Zhirnov Andrey
//	This file is part of the "UX Standard Lib" project.
//	See copyright notice in "UX_STL_Core.h".
//-------------------------------------------------------------------

#ifndef _UXS_STRING_UTILS_H
#define _UXS_STRING_UTILS_H

#pragma once

#include "../Containers/TString.h"
#include "../Containers/TUniArray.h"

namespace UX_STL
{
namespace UXTypes
{
namespace StringUtils
{

	//
	// IsUpper, IsLower
	//

	template <typename T>
	inline bool IsUpper(const T &c)
	{
		return ( c >= T('A') and c <= T('Z') );
	}


	template <typename T>
	inline bool IsLower(const T &c)
	{
		return ( c >= T('a') and c <= T('z') );
	}


	//
	// ToUpper, ToLower
	//

	template <typename T>
	inline T ToUpper(const T &c)
	{
		return ( IsLower( c ) ? c + T('A' - 'a') : c );
	}


	template <typename T>
	inline T ToLower(const T &c)
	{
		return ( IsUpper( c ) ? c + T('a' - 'A') : c );
	}


	template <typename T>
	inline TUniString<T> &  ToUpper(TUniString<T> &str)
	{
		for(usize i = 0; i < str.Length(); ++i) {
			str[i] = ToUpper( str[i] );
		}
		return str;
	}


	template <typename T>
	inline TString<T> &  ToUpper(TString<T> &str)
	{
		TUniString<T>	ustr( str );
		ToUpper( ustr );
		return str;
	}


	template <typename T>
	inline TUniString<T> &  ToLower(TUniString<T> &str)
	{
		for(usize i = 0; i < str.Length(); ++i) {
			str[i] = ToLower( str[i] );
		}
		return str;
	}


	template <typename T>
	inline TString<T> &  ToLower(TString<T> &str)
	{
		TUniString<T>	ustr( str );
		ToLower( ustr );
		return str;
	}


	template <typename T>
	inline bool GetNames(TString<T> &sFirst, TString<T> &sSecond, const T *pName)
	{
		const T	*	p = pName;

		while ( p != 0 )
		{
			if ( *p == '.' )
			{
				sFirst.Copy( pName, p );
				sSecond.Copy( p+1 );
				return true;
			}

			++p;
		}
		return false;
	}

	
	template <typename T, typename B>
	usize IntToStr(B val, T * buf, usize size, int radix)
	{
		if ( radix < 1 or radix > 16 )
		{
			WARNING( "incorrect radix" );
			radix = 10;
		}

		const usize		buf_size = ((sizeof(B) << 3) + 2);
		T				tmp_buf[ buf_size ] = {0};
		usize			i = buf_size - 2;
		bool			sign = ( radix == 10 and val < B(0) );

		val = UXMath::Abs( val );
	
		if ( val == 0 )
		{
			tmp_buf[i--] = T('0');
		}
		else
		{
			for(; val and i; --i, val /= radix)
			{
				tmp_buf[i] = T( "0123456789ABCDEF"[ val % radix ] );
			}
		}

		if ( sign )
			tmp_buf[i--] = '-';

		MemCopy( buf, tmp_buf + i + 1, buf_size - i - 1 );
		i = buf_size - i - 2;

		ASSERT( size > i );
		return i;
	}
	
	

	inline int		ToInt32(uni_c_string sStr)			{ return _hidden_::atoi( sStr.cstr() ); }
	inline int		ToInt32(const char *pStr)			{ return _hidden_::atoi( pStr ); }

#ifdef COMPILER_MSVC
	inline int64	ToInt64(uni_c_string sStr)			{ return _hidden_::_atoi64( sStr.cstr() ); }
	inline int64	ToInt64(const char *pStr)			{ return _hidden_::_atoi64( pStr ); }
#endif

#ifdef COMPILER_GCC
	inline int64	ToInt64(uni_c_string sStr)			{ return _hidden_::atoll( sStr.cstr() ); }
	inline int64	ToInt64(const char *pStr)			{ return _hidden_::atoll( pStr ); }
#endif

	inline float	ToFloat(uni_c_string sStr)			{ return float( _hidden_::atof( sStr.cstr() ) ); }
	inline float	ToFloat(const char *pStr)			{ return float( _hidden_::atof( pStr ) ); }
	inline double	ToDouble(uni_c_string sStr)			{ return _hidden_::atof( sStr.cstr() ); }
	inline double	ToDouble(const char *pStr)			{ return _hidden_::atof( pStr ); }


	template <typename T>
	inline int ToInt32(const TUniString<const T> &sStr)
	{
		string	s_str;
		s_str.Copy( sStr );
		return ToInt32( s_str );
	}


	template <typename T>
	inline int64 ToInt64(const TUniString<const T> &sStr)
	{
		string	s_str;
		s_str.Copy( sStr );
		return ToInt64( s_str );
	}


	template <typename T>
	inline float ToFloat(const TUniString<const T> &sStr)
	{
		string	s_str;
		s_str.Copy( sStr );
		return ToFloat( s_str );
	}


	template <typename T>
	inline double ToDouble(const TUniString<const T> &sStr)
	{
		string	s_str;
		s_str.Copy( sStr );
		return ToDouble( s_str );
	}

	
	template <typename T, typename S, typename A>
	inline TString<T,S,A> & NanoTimeToStr (INOUT TString<T,S,A> &sToStr, float fTimeNano)
	{
		if ( fTimeNano > 1.0e+9f)
			return sToStr << TString<T,S,A>().FormatF( fTimeNano * 1.0e-9f, StringFormatF().Fmt(0,2) ) << " s";
		else
		if ( fTimeNano > 1.0e+6f )
			return sToStr << TString<T,S,A>().FormatF( fTimeNano * 1.0e-6f, StringFormatF().Fmt(0,2) ) << " ms";
		else
		if ( fTimeNano > 1.0e+3f )
			return sToStr << TString<T,S,A>().FormatF( fTimeNano * 1.0e-3f, StringFormatF().Fmt(0,2) ) << " us";
		else
			return sToStr << TString<T,S,A>().FormatF( fTimeNano, StringFormatF().Fmt(0,2) ) << " ns";
	}

	
	template <typename T, typename S, typename A>
	inline TString<T,S,A> & TimeToStr (INOUT TString<T,S,A> &sToStr, double time)
	{
		if ( time > 1.0e-1 )
			return sToStr << TString<T,S,A>().FormatF( time, StringFormatF().Fmt(0,2) ) << " s";
		else
		if ( time > 1.0e-4 )
			return sToStr << TString<T,S,A>().FormatF( time * 1.0e+3, StringFormatF().Fmt(0,2) ) << " ms";
		else
		if ( time > 1.0e-7 )
			return sToStr << TString<T,S,A>().FormatF( time * 1.0e+6, StringFormatF().Fmt(0,2) ) << " us";
		else
			return sToStr << TString<T,S,A>().FormatF( time * 1.0e+9, StringFormatF().Fmt(0,2) ) << " ns";
	}


	template <typename T>
	inline uint64 FromStr16ToInt(const TUniString<const T> &s)
	{
		uint64	val  = 0;

		for (usize i = 0; i < s.Length(); ++i)
		{
			const T	c = s[i];

			val = (val << 4) + ( c >= T('0') and c <= T('9') ? c - T('0') : (
								 c >= T('a') and c <= T('f') ? c - T('a') + 10 : (
								 c >= T('A') and c <= T('F') ? c - T('A') + 10 : 0 ) ) );
		}
		return val;
	}


	template <typename T>
	inline int64 FromStr10ToInt(const TUniString<const T> &s)
	{
		int64	val  = 0;
		bool	sign = (s[0] == '-');

		for (usize i = (s[0] == '-' or s[0] == '+'); i < s.Length(); ++i)
		{
			const T	c = s[i];

			val = (val * 10) + ( c >= T('0') and c <= T('9') ? c - T('0') : 0 );
		}
		return sign ? -val : val;
	}


	template <typename T>
	inline double FromStrToFloat(const TUniString<const T> &s)
	{
		double	val		= 0;
		double	div		= 0;
		int32	dcount	= 0;
		int32	pow		= 0;
		bool	sign	= (s[0] == '-');
		bool	spow	= false;
		usize	i		= sign or s[0] == '+';

		for (; i < s.Length(); ++i)
		{
			const T	c = s[i];

			if ( c == '.' ) break;
			val = (val * 10.0) + ( c >= T('0') and c <= T('9') ? c - T('0') : 0 );
		}

		if ( s[i] == '.' )
		{
			++i;

			for (; i < s.Length(); ++i)
			{
				const T c = s[i];

				if ( c == 'e' or c == 'E' ) break;

				div = (div * 10.0) + ( c >= T('0') and c <= T('9') ? c - T('0') : 0 );
				++dcount;
			}
		}

		if ( s[i] == 'e' or s[i] == 'E' )
		{
			++i;
			spow = (s[i] == '-');

			i += ( spow or s[i] == '+' );

			for (; i < s.Length(); ++i)
			{
				const T	c = s[i];

				pow = (pow * 10) + ( c >= T('0') and c <= T('9') ? c - T('0') : 0 );
			}
		}

		return	( UXMath::SetSign( val, sign ) + div * UXMath::Pow10<double>( - dcount ) ) *
				  UXMath::Pow10<double>( UXMath::SetSign( pow, spow ) );
	}


	template <typename T>
	inline usize FromStrToNumber(const TUniString<const T> &str, float64_t &sNumber, bool &bIsInt)
	{
		// return code
		const usize	SUCCESS	= (usize)-1;

		if ( not (str[0] >= '0' and str[0] <= '9') and str[0] != '-' and str[0] != '+' )
			return 0;

		
		bool	sign	= (str[0] == '-');
		usize	i		= (sign or str[0] == '+');
		double	dval	= 0;


		if ( str.Length() > i+2 and str[i] == '0' and
			( str[i+1] == 'x' or str[i+1] == 'X' ) )
		{
		// Int 16bit
			uint64	ival	= 0;

			i   += 2;

			for (; i < str.Length(); ++i)
			{
				if ( str[i] >= '0' and str[i] <= '9' )		(ival <<= 4) += str[i] - '0';		else
				if ( str[i] >= 'a' and str[i] <= 'f' )		(ival <<= 4) += str[i] - 'a'+10;	else
				if ( str[i] >= 'A' and str[i] <= 'F' )		(ival <<= 4) += str[i] - 'A'+10;
				else										return i;
			}

			bIsInt = true;
			sNumber.SetI( UXMath::SetSign( (int64)ival, sign ) );
			return SUCCESS;
		}
		else
		// Int 10bit
		{
			uint64	ival	= 0;

			for (; i < str.Length(); ++i)
			{
				if ( str[i] >= '0' and str[i] <= '9' )		(ival *= 10) += str[i] - '0';
				else										break;
			}

			if ( i >= str.Length() ) {
				bIsInt = true;
				sNumber.SetI( UXMath::SetSign( (int64)ival, sign ) );
				return SUCCESS;
			}

			dval = (double) ival;
		}

		if ( str[i] == '.' )
		{
			double	div		= 0;
			int		dcount	= 0;
			int		ipow	= 0;
			bool	signpow	= false;

			++i;

			// Float
			for (;  i < str.Length(); ++i)
			{
				if ( str[i] >= '0' and str[i] <= '9' )
				{
					(div *= 10.0) += str[i] - '0';
					++dcount;
				}
				else
				if ( str[i] == 'e' or str[i] == 'E' )	{ ++i;	break; }
				else									return i;
			}

			if ( str[i] == '-' )	{ ++i;	signpow = true; }	else
			if ( str[i] == '+' )	{ ++i;	signpow = false; }

			for (; i < str.Length(); ++i)
			{
				if ( str[i] >= '0' and str[i] <= '9' )		(ipow *= 10) += str[i] - '0';	
				else										return i;
			}

			bIsInt = false;
			sNumber.SetF( UXMath::SetSign( dval + div * UXMath::Pow10<double>( -dcount ), sign ) *
						  UXMath::Pow10<double>( UXMath::SetSign( ipow, signpow ) ) );
			return SUCCESS;
		}

		return i;
	}

	
	template <typename T>
	inline const TUniString<const T> GetFieldName(const TUniString<const T> &str)
	{
		FOR_rev( i, str ) {
			if ( str[i] == T('.') or str[i] == T('>') )	// find '.' or '->'
				return TUniString<const T>( ( str.cstr() + i+1 ), str.Length() - i-1 );
		}
		return str;
	}


	inline const string  ToMetaFieldName(uni_c_string sFieldName)
	{
		// return formatted text, from "_someFiled" or "SomeField" to "some_field".

		const uni_c_string	name = GetFieldName( sFieldName );
		string				str;

		if ( name.Empty() )
			return str;

		FOR_range( i, 0, name.Length() )
		{
			const char	c		= name[i];
			const bool	upper	= IsUpper( c );
			const bool	lower	= IsLower( c );

			if ( str.Empty() and ( upper or lower ) )
				str << ToLower( c );
			else
			if ( i > 0 and upper and IsUpper( name[i-1] ) )
				str << ToLower( c );
			else
			if ( upper )
				str << '_' << ToLower( c );
			else
			if ( lower )
				str << c;
			else
			if ( c == '_' and not str.Empty() )
				str << c;
			else
			if ( c >= '0' and c <= '9' )
				str << c;
			else
				str.Clear();
		}

		return str;
	}


	inline const string  ToMetaTypeName (uni_c_string sTypeName)
	{
		// return formatted text, from "NS::CTypeName" to "TypeName"

		string	str;

		FOR_range( i, 0, sTypeName.Length() )
		{
			if ( sTypeName[i] == ':' )
			{
				str.ClearMem();
				continue;
			}
			else
			if ( str.Empty() and sTypeName.Length() > i+2 and
				 sTypeName[i] == 'C' and IsUpper( sTypeName[i+1] ) )
			{
				continue;
			}

			str << sTypeName[i];
		}

		return str;
	}

//-------------------------------------------------------------------

}	// StringUtils
}	// UXTypes
}	// UX_STL

#endif	// _UXS_STRING_UTILS_H